#include <cstdarg>
#include "vl_props.h"
#include "vl_prop_writer.h"
#include "vl_prop_reader.h"

using namespace VL;

namespace VL {
class VLPropContainer {

};
}

VLPropExcetion::VLPropExcetion(int errorCode, const char * msg, ...): errorCode(errorCode) {
	va_list va;
	va_start(va, msg);
	va_list copy;
	va_copy(copy, va);
	auto size = vsnprintf(nullptr, 0, msg, copy);
	va_end(copy);
	char buf[size + 1];
	vsnprintf(buf, size + 1, msg, va);
	va_end(va);
	buf[size] = '\0';
	this->message = buf;
}

VLProp VLProp::emptyPtop = VLProp();

VLProp::VLProp(): dataPointer(nullptr), extraPointer(nullptr), type(VLPropType::Empty), valueType(VLPropValueType::Void) {

}

VLPropType VLProp::getPropType() const {
	return this->type;
}

VLPropValueType VLProp::getValueType() const {
	return this->valueType;
}

const bool VLProp::operator == (const VLProp& prop) const {
	if(this->type != prop.type) {
		return false;
	}
	switch(this->type) {
		case VLPropType::Node:
			return this->_checkNodeEqual(prop);
		case VLPropType::Array:
			return this->_checkArrayEqual(prop);
		case VLPropType::Map:
			return this->_checkMapEqual(prop);
		default:
			return true;
	}
}

const bool VLProp::checkValueEqual (const VLProp& prop) const {
	if(this->type != prop.type) {
		if(this->type == VLPropType::Empty || prop.type == VLPropType::Empty) {
			const VLProp* notEmptyPtr = this->type == VLPropType::Empty ? &prop : this;
			switch(this->type) {
				case VLPropType::Array:
				case VLPropType::Map:
					return this->size() == 0;
				default:
					break;
			}
		}
		return false;
	}
	switch(this->type) {
		case VLPropType::Node:
			return this->_checkNodeEqual(prop);
		case VLPropType::Array:
			return this->_checkArrayEqual(prop);
		case VLPropType::Map:
			return this->_checkMapEqual(prop);
		default:
			return true;
	}
}

const bool VLProp::operator != (const VLProp& prop) const {
	return !this->operator==(prop);
}

void VLProp::unique() {
	switch (this->type) {
		case VLPropType::Array:
			this->_uniqueArr();
			break;
		case VLPropType::Map:
			this->_uniqueMap();
			break;
		default: break;
	}
	return;
}

void VLProp::addComment(const std::string& comment) {
	if(this->extraPointer.get() == nullptr) {
		this->extraPointer.reset(new std::vector<std::string>());
	}
	this->extraPointer->push_back(comment);
}

void VLProp::addComments(const std::vector<std::string>& commentList) {
	if(this->extraPointer.get() == nullptr) {
		this->extraPointer.reset(new std::vector<std::string>());
	}
	this->extraPointer->insert(this->extraPointer->end(), commentList.begin(), commentList.end());
}

const bool VLProp::isArray() const {
	return this->type == VLPropType::Array;
}
const bool VLProp::isMap() const {
	return this->type == VLPropType::Map;
}
const bool VLProp::isNode() const {
	return this->type == VLPropType::Node;
}
const bool VLProp::isEmpty() const {
	return this->type == VLPropType::Empty;
}

const size_t VLProp::size() const {
	switch(this->type) {
		case VLPropType::Array:
			return this->_getArraySize();
		case VLPropType::Map:
			return this->_getMapSize();
		default:
			return 0;
	}
}

bool VLProp::combine(const VLProp& prop, bool deepCombine) {
	// printf("combine: %s & %s\n", VLProp::_getPropTypeStr(this->type), VLProp::_getPropTypeStr(prop.type));
	switch(this->type) {
		case VLPropType::Node: {
			// 待合并属性为 node
			switch(prop.type) {
				case VLPropType::Node: {
					// 被合并属性也是 node, 合并成为 array
					VLProp prop1;
					prop._copyNode(prop1);
					*this = vArr(*this, prop1);
					return true;
				}
				case VLPropType::Array: {
					// 被合并属性是 array, 并入到其中
					VLProp prop1;
					prop._copyArray(prop1);
					prop1.insert(0, *this);
					* this = prop1;
					return true;
				}
				case VLPropType::Map:
					// node 和 map 不能直接合并
					return false;
				default: return false;
			}
		}

		case VLPropType::Array: {
			// 待合并属性为 array
			switch(prop.type) {
				case VLPropType::Node: {
					// 被合并属性是 Node, 并入到其中
					VLProp prop1;
					prop._copyNode(prop1);
					this->add(prop1);
					return true;
				}
				case VLPropType::Array: {
					// 被合并属性是 Array, 合并同类项
					prop.visitArr([&](const VLProp& child) -> bool {
						if(child.getPropType() == VLPropType::Node) {
							VLProp prop1;
							child._copyNode(prop1);
							this->add(prop1);
						} else {
							// array 和 map 都添加引用
							this->add(child);
						}
						return true;
					});
					return true;
				}
				case VLPropType::Map: {
					// array 和 map 不能直接合并
					return false;
				}
				default: return false;
			}
		}

		case VLPropType::Map: {
			// 待合并属性为 Map
			switch(prop.type) {
				case VLPropType::Map: {
					prop.visitMap([&](const std::string& key, const VLProp& child) -> bool {
						if(deepCombine) {
							if(child.getPropType() == VLPropType::Node) {
								VLProp prop1;
								child._copyNode(prop1);
								this->operator[](key).combine(prop1);
							} else {
								this->operator[](key).combine(child);
							}
						} else {
							auto & childProp = this->operator[](key);
							if(childProp.isEmpty()) {
								if(child.getPropType() == VLPropType::Node) {
									child._copyNode(childProp);
								} else {
									childProp = child;
								}
							}
						}
						return true;
					});
					return true;
				}
				// node 和 array 都不能与 map 合并
				default: return false;
			}
		}

		case VLPropType::Empty: {
			prop.copy(*this);
			return true;
		}
	}

	return false;
}

const void VLProp::copy(VLProp& targetProp) const {
	switch(this->type) {
		case VLPropType::Node: {
			this->_copyNode(targetProp);
			break;
		}
		case VLPropType::Array: {
			this->_copyArray(targetProp);
			break;
		}
		case VLPropType::Map: {
			this->_copyMap(targetProp);
			break;
		}
		default: {
			break;
		}
	}
}

const void VLProp::print() const {
	switch(this->type) {
		case VLPropType::Node:
			this->_printNode("");
			break;
		case VLPropType::Array:
			this->_printArray("");
			break;
		case VLPropType::Map:
			this->_printMap("");
			break;
		case VLPropType::Empty:
			printf("Empty\n");
			break;
	}
}

const void VLProp::reset() {
	this->dataPointer.reset();
	this->extraPointer.reset();
	this->type = VLPropType::Empty;
	this->valueType = VLPropValueType::Void;
}

const void VLProp::writeVLC(VLPropWriter& writer) const {
	this->_writeVLC(writer);
}

const void VLProp::_writeVLC(VLPropWriter& writer) const {
	switch(this->type) {
		case VLPropType::Node:
			this->_writeNodeVLC(writer);
			break;
		case VLPropType::Array:
			this->_writeArrayVLC(writer);
			break;
		case VLPropType::Map:
			this->_writeMapVLC(writer);
			break;
		default: break;
	}
}

const void VLProp::_writeEmptyVLC(VLPropWriter& writer) const {
	writer.write((uint8_t)this->type & 0xFF);
	auto extra = this->extraPointer.get();
	if(extra != nullptr) {
		auto extraSize = extra->size();
		writer.write(extraSize, 2);
		for(auto iter = extra->begin() ; iter != extra->end() ; iter ++) {
			writer.writeStrWithSize(*iter);
		}
	} else {
		writer.write(0, 2);
	}
}

const void VLProp::_readEmptyVLC(VLPropReader& reader) {
	this->dataPointer.reset();
	this->extraPointer.reset();
	this->type = VLPropType::Empty;
	this->valueType = VLPropValueType::Void;
	this->_readExtraVLC(reader);
}

const void VLProp::_readExtraVLC(VLPropReader& reader) {
	auto size = reader.readNumber<uint16_t>();
	if(size > 0) {
		std::string tempStr;
		for(uint16_t i = 0 ; i < size ; i ++) {
			reader.readStrWithSize(tempStr);
			this->addComment(tempStr);
		}
	}
}

const void VLProp::writeVLP(VLPropWriter& writer) const {
	this->_writeVLP(writer);
}

const void VLProp::_writeVLP(VLPropWriter& writer) const {
	switch(this->type) {
		case VLPropType::Node:
			this->_writeNodeVLP(writer, "");
			break;
		case VLPropType::Array:
			this->_writeArrayVLP(writer, "");
			break;
		case VLPropType::Map:
			this->_writeMapVLP(writer, "");
			break;
		default: break;
	}
}

const char * VLProp::_getValueTypeStr(VLPropValueType valueType) {
	switch(valueType) {
		// 空值
		case VLPropValueType::Void:
			return "void";
		// 字符串
		case VLPropValueType::String:
			return "string";
		// 布尔值
		case VLPropValueType::Boolean:
			return "boolean";
		// 字节
		case VLPropValueType::Byte:
			return "byte";
		// 无符号字节
		case VLPropValueType::UByte:
			return "unsigned byte";
		// 短整型
		case VLPropValueType::Short:
			return "short";
		// 无符号短整型
		case VLPropValueType::UShort:
			return "unsigned short";
		// 整数
		case VLPropValueType::Int:
			return "int";
		// 无符号整数
		case VLPropValueType::UInt:
			return "unsigned int";
		// 长整型
		case VLPropValueType::Long:
			return "long";
		// 无符号长整型
		case VLPropValueType::ULong:
			return "unsigned long";
		// 双精度浮点型
		case VLPropValueType::Double:
			return "double";
	}
	return "unknown";
}

const char * VLProp::_getPropTypeStr(VLPropType propType) {
	switch(propType) {
		case VLPropType::Node:
			return "Node";
		case VLPropType::Map:
			return "Map";
		case VLPropType::Array:
			return "Array";
		case VLPropType::Empty:
			return "Empty";
	}
	return "unknown";
}